Dans le cadre de la création du langage de marquage MarkIt, il était indispensable pour moi de générer des documents aléatoires afin de tester toutes les fonctionnalités du programme et de vérifier la mise en page.
Il était donc nécessaire de créer un générateur prenant en compte les spécificités de du langage et éviter les incohérences de structure du document:
Limitation de la profondeur des listes
Pas d'en-têtes dans les listes, les tableaux, les notes, …
…
La bibliothèque quickcheck
permet bien de générer des valeurs aléatoires avec différents modificateurs mais pas de modifier l'état des générateurs en cours de fonctionnement.
Pour pouvoir le faire, il faut donc passer par une bibliothèque gérant les états (transformers
par exemple) et plus particulièrement le modificateur StateT
qui permet de créer une monade state "modifiée" contenant une monade d'un autre type.
Supposons que l'on veuille générer des structures de données avec le type suivant en limitant la profondeur, en forçant le premier élément à être un en-tête et avec des générateurs spécifiques à chaque niveau de profondeur.
Il nous faut donc un état qui contiendra les différents paramètres permettant de modifier les générateurs en cours d'exécution:
On utilise le type StateT
pour modifier et créer un nouveau monad embarquant l'état MyState
et permettant de faire tournant des monads Gen
(provenant de la bibliothèque quickcheck
) à l'intérieur.
Le lancement des fonctions de générations à l'intérieur de la monade MyGen
se fait à l'aide de la fonction lift
Il sera nécessaire de faire des versions spécifiques de certaines fonctions (transformers) de la librairie quickcheck
pour pouvoir les utiliser avec la nouvelle monade MyGen
. Il faudra réécrire les fonctions décrites dans le module Test.QuickCheck.Gen.
Une fois ce travail fait, on peut générer des strcutures aléatoires en combinant les fonctions generate
du module quickcheck
et evalStateT
du module
et on peut générer les structures souhaitées: